// 2.duck typing
// interface Params {
//     a: string
// }
// function fn(obj: Params) {}
// let c = {a: 'zs', name: '1111'}
// fn(c)

// 可选参数
// interface Params {
//     name?: string
// }
// function fn(obj: Params) {}
// fn({})

// 这种模式下可选参数已经失去了意义，为了挽救可选参数，解决方法有如下两种
// as语法
// interface params {
//     a?: string,
//     b?: number
// }
// function fn (obj: params): void {}
// fn({b: 1, c: 'bb'} as params)

// 索引签名
// interface params {
//     a?: string,
//     b?: number,
//     [propName: string]: any
// }
// function fn (obj: params): void {}
// fn({a: 'zs', c: true, d: '20'})


// 3. 函数类型的验证
// 函数的验证使用接口来实现会更加的简洁
// interface myFn {
//     (a: string, b: number): void
// }
// let fn: myFn = (name, age) => {}
// fn('aa', 11)
// 在定义函数时，函数的参数可以写类型也可以不写，并且参数的名称和接口的参数名称不需要相同

// 注意：在javascript中，函数其实就是一个对象，所以函数的验证可以存在即是对象又是函数的情况，如下
// interface Test {
//     (a: string): string;
//     b: number;
// }
// function fn(): Test {
//     let fn = function (a: string) { return a } as Test
//     fn.b = 123
//     return fn
// }

// 函数验证的其他写法
// let fn: (a: number) => void = function (x: number): void { }
// let fn1 = function (x: number): void { }
// let fn2: (a?: number) => void = function (x) { } // 可选参数
// function fn3(a: number | string) { } // 多种参数类型


// 4. 索引验证
interface Entries {
    [index: number]: number;
}
let arr: Entries = [1, 2, 3, 4]

// 同样还支持只读的索引
interface ReadonlyStringArray {
    readonly [index: number]: string
}
let strArr: ReadonlyStringArray = ['zs', 'ls', 'ww']


// 5. 接口扩展
// 接口可以继承其他接口
interface Test1 {
    a: string
}
interface Test2 {
    b: string
}
interface Test3 extends Test1, Test2 { }
let a: Test3 = { a: 'zs', b: 'ls' }

// 接口集成其他类
// class Demo {
//     public a: string;
//     private b: number;
//     protected c: string;
// }
// interface Test extends Demo {}
// class Demo1 extends Demo implements Test{
//     public a: "aa";
// }
// 注意，由于接口继承类，接口可以继承类的private和protected成员，所以子类继承此接口要同时继承父类才行


// 6. 泛型
// 就是类型参数
// 泛型函数
function fn<T>(params: T): T {
    return params
}
fn<string>('aa'); // 显式调用
fn('aa') // 隐式调用
// 完整写法
let fn1: <T>(params: T) => T = function <T>(parmas: T): T {
    return parmas
}

// 泛型接口
// 不提取参数
interface Itf {
    <T>(params: T): T
}
let fn2: Itf = function <T>(params: T): T {
    return params
}

// 提取参数
interface Itf1<T> {
    (params: T): T
}
let fn3: Itf1<string> = function <T>(params: T): T {
    return params
}

// 泛型类
class Demo2<T>{
    fn: (a: T) => T;
}

// 泛型约束
// 一般写法
interface Demo {
    length: number
}
function fn4<T extends Demo>(params: T): T {
    console.log(params.length);
    return params
}
fn<string>('aa')

// 相互约束
function fn5<T, K extends keyof T>(obj: T, key: K) {
    console.log(obj[key]);
    return obj[key]
}
fn5({ a: 1 }, 'a')


// 7. 与第三方库一起使用
// 很多基于npm的第三方包，都没有使用typescript，那么在ts中想使用这些安装包，需要遵从如下操作
// 第一，在项目中新建一个.d.ts文件，如 third.d.ts 
// 第二，在文件中定义模块，如
declare module "url" { // 此模块名必须和第三方包的名字一样
    export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
    }
    export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}
declare module "path" {
    export function normalize(p: string): string;
    export function join(...paths: any[]): string;
    export var sep: string;
}
declare module "hot-new-module";  // 不想写声明可以不写
// 第三，在需要使用第三方包的文件中导入模块
/// <reference path="third.d.ts"/> 导入.d.ts声明文件
// import * as URL from "url"; // 导入模块
// let myUrl = URL.parse("http://www.typescriptlang.org");


// 8. 命名空间
// 命名空间和js的对象一样，单纯的为了避免命名冲突
namespace Demo {
    export let a = 'aa';
}
window.console.log(Demo.a);


// 9. react组件属性验证
// 使用React typings注入
// interface Props {
//     a: string;
// }
// class App extends React.Component<Props, {}> {
//     render() {
//         return (
//             <div className="App">
//                 { this.props.a }
//             </div>
//         )
//     }
// }
// 使用此组件
// <App a="aa" />
